功能需求寫「應該發生什麼」,安全需求寫「不能發生什麼」。後者 AI 不會主動替你寫。
小安(第 1 天情境裡的那位)想替他的預約網站加一個功能。他跟 AI 說:
幫我加一個「把預約分享給朋友」的功能,朋友點連結就能看到預約時間。
十分鐘後功能做好了,測試也通過:點分享、複製連結、朋友打開、看得到時間。
他沒有問、AI 也沒有提的問題是:
這些問題的答案,決定了這個功能是「分享」還是「外洩」。
| 面向 | 功能需求 | 安全需求 |
|---|---|---|
| 句型(以分享為例) | 使用者可以分享預約 | 沒拿到分享連結的人不能看到預約 |
| 誰會提出來 | 你會說,AI 會做 | 你通常不會說,AI 有時會補、有時不會 |
| 怎麼測 | 做一次,成功就好 | 要把所有「不該成功」的方法試過一輪 |
| 什麼時候發現缺了 | 開發當下 | 通常是上線之後 |
在 SSDLC 裡,需求與設計階段的工作,就是把安全需求在寫程式之前寫出來。因為改一份規格只要五分鐘,改一個已經上線、有兩百個使用者的服務,可能要一個週末加一封道歉信。
一般的使用情境(use case)問「使用者想做什麼」。Abuse case 問的是:「一個不懷好意的人,會怎麼用這個功能?」
| 功能 | 正常用法 | 濫用方式 |
|---|---|---|
| 分享預約 | 傳給朋友看時間 | 改連結裡的編號,看到別人的預約 |
| 手機簡訊驗證 | 註冊時收驗證碼 | 一直按「寄送」,讓你的簡訊費暴增(第 27 天) |
| AI 幫我寫文案 | 一天用幾次 | 寫程式一直按,把你的服務當成免費的 AI(第 27 天) |
| 優惠碼 | 輸入一次折 100 元 | 同時送出 20 次,折 20 次 |
| 上傳大頭貼 | 上傳一張照片 | 上傳一個 2GB 的檔案,或一個偽裝成圖片的網頁 |
把右邊那一欄反過來寫成「不能……」的句子,就是你要交給 AI 的安全需求。
這個系列用 SSDLC 決定什麼時候做,用五個思考模型決定做的時候問什麼。它們不是只在今天用,接下來 28 篇的每一篇,都會標出它主要用到哪一個。
| 思考模型 | 要問的問題 | 用在分享功能上 |
|---|---|---|
| 信任邊界 | 這份資料從哪裡來?誰能控制它? | 分享連結裡的編號,使用者改得動嗎? |
| 身分與權限 | 確認了是誰之後,有沒有確認他能做這件事? | 拿到連結就能看,還是要登入並確認是被分享的人? |
| 資料被當成程式執行 | 這段內容,會不會被某個東西執行? | 預約備註會顯示在朋友的畫面上,如果備註裡是一段 <script> 呢? |
| 狀態與時間 | 過了一段時間、或同時發生很多次,還成立嗎? | 取消分享之後,舊連結還有效嗎? |
| 預設值與依賴 | 我沒特別設定的東西,現在是什麼狀態? | 新增的 shares 資料表,預設誰都讀得到嗎? |
五個問題問完,通常就能列出一半以上的 abuse case。剩下的,交給下面的 STRIDE。
挑你服務裡最重要的一個功能,回答:
這段提示詞放在請 AI 實作新功能之前。重點是最後一句:AI 預設會直接開工,要讓它先交設計。
在開始實作之前,請先輸出這個功能的 threat model,不要寫任何程式碼:
1. 資料流:使用者 → 前端 → API → 資料庫/第三方服務,標出每一個 trust boundary
2. 針對每一個 trust boundary,用下面五個問題列出 abuse case:
- 這份資料從哪裡來?誰能控制它?
- 確認了是誰之後,有沒有確認他能做這件事?
- 這段內容,會不會被某個東西執行(資料庫、瀏覽器、AI)?
- 過了一段時間、或同時發生很多次,還成立嗎?
- 沒有特別設定的東西(資料表權限、預設值、套件),現在是什麼狀態?
3. 每一個 abuse case:對應的防護措施,以及一個「證明防護有效」的測試案例(寫成「用某身分做某事,應該被拒絕」)
列完後停下來,等我確認再開始實作。
功能需求描述「應該發生什麼」,可以直接變成程式碼和測試。安全需求大多描述「不能發生什麼」,它的測試空間是無限的:不能越權、不能注入、不能 replay、不能被濫用。
LLM 生成程式碼時,主要依據提示詞裡明說的目標,再加上訓練資料裡常見的寫法。我的觀察是:教學範例、quick start 文件為了簡潔,最常省略的正是 authorization、rate limit、錯誤處理,所以 AI 產出的程式碼,常常像一份很完整的教學範例,離能上線的服務還差一段。
研究也看到類似的結果。2022 年發表在 IEEE S&P 的研究,讓 GitHub Copilot 在 89 個跟 CWE Top 25 相關的情境裡補完程式碼,產出的 1,689 個程式約 40% 有漏洞。Veracode 在 2025 年 7 月測了 100 多個 LLM,45% 的程式碼樣本沒通過安全測試,而且模型越大、越新,安全表現也沒有明顯變好。
工程師用 AI 時也有同樣的問題:你心裡知道要檢查權限,但提示詞裡沒寫;review 時又因為產出量太大,沒有逐行看。Stanford 的使用者研究(CCS 2023)發現,能用 AI 助手的受試者寫出的程式碼比較不安全,卻更相信自己寫的是安全的。threat model 的價值,是把「心裡知道」變成「寫在紙上、AI 讀得到、測試對得到」。
Threat modeling 的第一步是畫資料流圖(data flow diagram,DFD)。不需要工具,紙筆就可以。以小安的分享功能為例:

圖上虛線框起來的 trust boundary,就是第一個思考模型「信任邊界」畫在圖上的樣子。
每一條穿過 trust boundary 的箭頭,都是一個要問問題的地方。瀏覽器在邊界外,所以從瀏覽器進來的每一個參數,都是不可信的;簡訊服務在邊界外,所以每一次呼叫都是一筆費用。
STRIDE 是 Microsoft 的 Loren Kohnfelder 和 Praerit Garg 在 1999 年提出的威脅分類,拿來檢查「還有沒有漏掉的類型」很好用。以這個系列的靶場「會員制待辦清單」為例(包含第 3 週會加上的重設密碼和管理後台):
| 威脅 | 意思 | 待辦清單裡的例子 | 對應篇章 |
|---|---|---|---|
| Spoofing | 冒充別人 | 重設密碼流程被利用,登入別人的帳號 | 第 14 天 |
| Tampering | 竄改不該改的資料 | 改網址 id,修改別人的待辦 | 第 4、19 天 |
| Repudiation | 做了壞事卻查不到 | 沒有 audit log,不知道誰刪了清單 | 第 29 天 |
| Information disclosure | 看到不該看的 | 資料庫沒設權限,清單全部外洩 | 第 5 天 |
| Denial of service | 讓服務不能用或變很貴 | 簡訊驗證、AI 按鈕被灌爆 | 第 27 天 |
| Elevation of privilege | 變成更高權限的角色 | 更新個人資料時順便把自己改成管理員 | 第 13 天 |
做法:在 DFD 上每一條穿過 trust boundary 的箭頭,把六個字母各問一次。五個思考模型負責「換個角度想」,STRIDE 負責「確認沒有漏掉整個類別」。
「要注意權限」不是需求,它沒辦法測。一條好的安全需求要能直接變成測試:
| 模糊的寫法 | 可測試的寫法 |
|---|---|
| 分享功能要安全 | 以隨便編造的 token 呼叫 GET /share/:token,回應 404;以有效 token 讀取,回應只有預約時間,不含電話與備註 |
| 要防止簡訊濫用 | 同一個手機號碼 60 秒內第二次請求驗證碼,回應 429,且簡訊服務沒有被呼叫 |
| 取消分享要有效 | 擁有者取消分享後,以舊 token 讀取,回應 404 |
| 要防止權限提升 | 一般會員以 PATCH /profile 送出 role=admin,資料庫中的 role 不變 |
右邊的句子,第 4 天會變成權限表,第 19 天會變成 regression test,第 22 天會變成 AI 審查時的「答案卷」。
自己想不到要寫哪些句子時,可以拿 OWASP ASVS(Application Security Verification Standard)當清單。
5.0 版在 2025 年 5 月發布,大約 350 條需求,分成 L1 到 L3 三個等級。整份對小型服務太多,下面挑五條跟這篇例子直接相關的,示範怎麼翻成你自己服務的句子:
| ASVS 5.0 條目(等級) | 條文重點 | 寫成小安服務的安全需求 |
|---|---|---|
| 8.2.2(L1) | 資料層級的存取,只開放給對那筆資料有明確權限的人,防止 IDOR | 已登入的使用者 A 以 GET /bookings/:id 讀取使用者 B 的預約,回應 404 |
| 15.3.1(L1) | API 只回傳需要的欄位,不回傳整個資料物件 | GET /share/:token 的回應只有 start_time、end_time 兩個欄位 |
| 15.3.3(L2) | 防止 mass assignment:每個動作只允許寫入它該寫的欄位 | 以 PATCH /profile 送出 role=admin 或 is_verified=true,資料庫中的值不變 |
| 2.4.1(L2) | 有 anti-automation 控制,防止功能被大量呼叫、耗盡額度或花掉昂貴資源 | 同一支手機一天內第 6 次請求驗證碼,回應 429,簡訊服務沒有被呼叫 |
| 2.3.4(L2) | 數量有限的資源(例如座位、時段)要有鎖定機制,不能被重複預約 | 兩個請求同時預約同一個時段,只有一個成功,資料庫裡只有一筆預約 |
簡訊的上限跟第 27 天的建議一致(每分鐘最多 1 封、每天最多 5 封),實際數字要依你的服務調整。
Threat model 不是寫一次就結束的文件。實務上:
docs/threat-model.md,跟程式碼一起進版控注意它的性質:threat model 是規格,讓 AI 和人知道要做什麼、測什麼。它不是安全邊界,真正擋住攻擊的是程式、權限設定和測試(第 8 天會談「告示」和「鎖」的差別)。
AI 會完成你說的功能,但不會替你想「壞人會怎麼用它」。在它動手之前,用五個問題把「不能發生的事」寫下來,寫成可以測試的句子。